package Dm

import (
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"db2s/ETL"
	"db2s/global"
	"math"
	"reflect"
	"sort"
	"strconv"
	"strings"
)

type TableInfoMeta struct {
}

func getTableRows(event string, s global.TablesMetaInfoInput) (tableRows global.RowsReturnResult, err error) {
	tableRows, err = TableSum{}.Rows(global.TableSumInput{
		LogSeq: s.LogSeq,
		TableInfo: global.TableInfo{
			Schema:           s.TableInfo.Schema,
			Table:            s.TableInfo.Table,
			BackendTableName: s.TableInfo.BackendTableName,
		},
		Db: s.DB,
	})
	if err != nil {
		err = errors.New(fmt.Sprintf("%v %v", event, err))
		return
	}
	return
}
func (or TableInfoMeta) AvgRowLength(s global.TablesMetaInfoInput) (res global.AvgRowLengthReturnResult, err error) {
	var (
		event     = "[oracleAvgRowLength]"
		f1        any
		tableRows global.RowsReturnResult
	)
	if tableRows, err = getTableRows(event, s); err != nil {
		return
	}
	res.BaseResult.Sql = fmt.Sprintf("select segment_name,segment_type,nvl(sum(bytes),0) as \"dataLength\" from dba_segments where OWNER='%v' and segment_type in ('TABLE','TABLE PARTITION')  AND segment_name='%v' group by segment_name,segment_type order by 3 DESC", s.TableInfo.Schema, s.TableInfo.Table)
	if res.BaseResult.TimeOut, f1, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: res.BaseResult.Sql,
		Expect:       ETL.Map,
	}); err != nil || f1 == nil {
		if err != nil {
			err = errors.New(fmt.Sprintf("%v %v", event, err))
		}
		return
	}
	if l, ok := f1.(map[string]any)["dataLength"]; ok {
		var result int64
		if result, err = strconv.ParseInt(fmt.Sprintf("%v", l), 10, 64); err != nil {
			err = errors.New(fmt.Sprintf("%v %v", event, err))
			return
		}
		if result > 0 && tableRows.Result > 0 {
			res.Result = result / int64(tableRows.Result)
		}
	}
	return
}

func (or TableInfoMeta) DataSize(s global.TablesMetaInfoInput) (result global.DataSizeReturnResult, err error) {
	var (
		event = "[oracleDataSize]"
		f1    any
	)
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.DB["single"], SqlMode: true}

	result.Sql = fmt.Sprintf("SELECT * FROM (SELECT ROUND(SUM(bytes) / 1048576,2) AS \"dataSizeMB\"  FROM DBA_SEGMENTS   WHERE owner = '%v' AND (SEGMENT_NAME LIKE 'SYS_LOB' || (SELECT LPAD(OBJECT_ID, 10, 0) FROM ALL_OBJECTS WHERE owner = '%v' AND OBJECT_NAME = '%v' AND object_type = 'TABLE') OR segment_name = '%v')) data, (SELECT ROUND(SUM(bytes) / 1048576,2) AS \"indexSizeMB\"  FROM DBA_SEGMENTS  WHERE OWNER = '%v' AND SEGMENT_NAME IN (SELECT INDEX_NAME FROM ALL_INDEXES WHERE TABLE_OWNER = '%v' AND TABLE_NAME = '%v'))", s.TableInfo.Schema, s.TableInfo.Schema, s.TableInfo.Table, s.TableInfo.BackendTableName, s.TableInfo.Schema, s.TableInfo.Schema, s.TableInfo.BackendTableName)
	if len(s.TableInfo.Schema) == 0 || len(s.TableInfo.BackendTableName) == 0 || len(s.DB) == 0 || active.DB == nil {
		err = errors.New(fmt.Sprintf("%v sql general fail. schema or table is entry. Execution process:{schema:%v,table:%v,sql:%v,shard:%v,db:%v,err:%v}", event, s.TableInfo.Schema, s.TableInfo.BackendTableName, result.Sql, s.TableInfo.ShardName, s.DB, err))
		return
	}
	if result.ExecTimeout, f1, err = active.SqlQuery(result.Sql, ETL.Map); err != nil {
		err = errors.New(fmt.Sprintf("%v sql exec fail. Execution process:{sql:%v,shard:%v,db:%v,err:%v}", event, result.Sql, "single", s.DB, err))
		return
	}
	if err = resultTypeCheck(event, ETL.Map, f1); err != nil || f1 == nil {
		if err == nil {
			err = errors.New(fmt.Sprintf("%v result is empty. Execution process:{sql:%v,shard:%v,db:%v,result:%v}", event, result.Sql, "single", s.DB, f1))
		}
		return
	}
	for k, v := range []string{"dataSizeMB", "indexSizeMB"} {
		if _, ok := f1.(map[string]interface{})[v]; !ok {
			err = errors.New(fmt.Sprintf("%v query result not include %v. process:{sql:%v,result:%v}", v, event, result.Sql, f1))
			return
		}
		v1 := fmt.Sprintf("%v", f1.(map[string]interface{})[v])
		if v1 == "<null>" {
			v1 = "0"
		} else if strings.HasPrefix(v1, ".") {
			v1 = fmt.Sprintf("0%v", v1)
		}
		if !isNumeric(v1) {
			err = errors.New(fmt.Sprintf("%v The result contains illegal non-numeric data. process:{sql:%v,result:%v}", event, result.Sql, v1))
			return
		}
		var v2 float64
		if v2, err = strconv.ParseFloat(v1, 64); err != nil {
			err = errors.New(fmt.Sprintf("%v string strconv float64 fail, strconv data is {%v} error is {%v}", event, v1, err))
			return
		}
		switch k {
		case 0:
			result.DataSizeMB = math.Round(v2*100) / 100
		case 1:
			result.IndexSizeMB = math.Round(v2*100) / 100
		}
	}
	return
}

// Comment 获取表的comment信息
func (or TableInfoMeta) Comment(s global.TablesMetaInfoInput) (result global.TableCommentReturnResult, err error) {
	var (
		event = "[oracleComment]"
		f1    any
	)
	for _, tableView := range []string{
		//"user_tab_comments",
		"all_tab_comments",
		"dba_tab_comments",
	} {
		result.BaseResult.Sql = fmt.Sprintf("SELECT comments as \"comment\" FROM %v WHERE OWNER = '%v' AND TABLE_NAME='%v'", tableView, s.TableInfo.Schema, s.TableInfo.Table)
		if result.BaseResult.TimeOut, f1, err = ExecSelectSql(ExecSqlInputS{
			Ss:           s,
			Event:        event,
			SelectSqlStr: result.BaseResult.Sql,
			Expect:       ETL.Map,
		}); err != nil || f1 == nil {
			continue
		}
		if v, ok := f1.(map[string]any)["comment"]; ok {
			switch {
			case strings.EqualFold(fmt.Sprintf("%v", v), "<null>"), strings.EqualFold(fmt.Sprintf("%v", v), "<entry>"), len(fmt.Sprintf("%v", v)) == 0:
				result.Result.Comment = "<entry>"
			default:
				result.Result.Comment = fmt.Sprintf("%v", v)
			}
			break
		}
	}
	return
}

// IfTableLob 判断表中是否存在lob字段
func (or TableInfoMeta) IfTableLob(s global.TablesMetaInfoInput) (result global.TableBigVarcharMeta, err error) {
	var (
		event                  = "[oracleIfTableLob]"
		res                    any
		columnName, columnType []string
	)
	result.BaseResult.Sql = fmt.Sprintf("select "+
		"column_name as \"columnName\","+
		"data_type as \"columnType\" "+
		"from all_tab_columns "+
		"where "+
		"OWNER = '%s' AND "+
		"TABLE_NAME = '%s' AND "+
		"data_type like '%%LOB%%'",
		s.TableInfo.Schema, s.TableInfo.BackendTableName)
	if result.BaseResult.TimeOut, res, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: result.BaseResult.Sql,
		Expect:       ETL.SMap,
	}); err != nil || res == nil {
		if err != nil {
			err = errors.New(fmt.Sprintf("%v %v", event, err))
		}
		return
	}
	for _, v := range res.([]map[string]any) {
		columnName = append(columnName, fmt.Sprintf("%v", v["columnName"]))
		columnType = append(columnType, fmt.Sprintf("%v", v["columnType"]))
	}
	if len(columnName) > 0 && len(columnType) > 0 {
		result = global.TableBigVarcharMeta{
			ColumnName: columnName,
			ColumnType: columnType,
		}
	}
	return

}

// IfBigVarchar 判断表是否存在varchar>1000
func (or TableInfoMeta) IfBigVarchar(s global.TablesMetaInfoInput) (result global.TableBigVarcharMeta, err error) {
	var (
		event                  = "[oracleIfBigVarchar]"
		columnName, columnType []string
		res                    any
	)
	result.BaseResult.Sql = fmt.Sprintf("SELECT COLUMN_NAME \"columnName\" ,DATA_TYPE as \"dataType\",DATA_LENGTH as \"dataLength\" FROM ALL_TAB_COLUMNS WHERE OWNER = '%s' and table_name = '%s' and DATA_TYPE = 'VARCHAR2' AND DATA_LENGTH >= %d", s.TableInfo.Schema, s.TableInfo.BackendTableName, 1000)
	if result.BaseResult.TimeOut, res, err = ExecSelectSql(ExecSqlInputS{
		Ss:           s,
		Event:        event,
		SelectSqlStr: result.BaseResult.Sql,
		Expect:       ETL.SMap,
	}); err != nil || res == nil {
		if err != nil {
			err = errors.New(fmt.Sprintf("%v %v", event, err))
		}
		return
	}
	for _, v := range res.([]map[string]any) {
		columnName = append(columnName, fmt.Sprintf("%v", v["columnName"]))
		columnType = append(columnType, fmt.Sprintf("%v(%v)", v["dataType"], v["dataLength"]))
	}
	if len(columnName) > 0 && len(columnType) > 0 {
		result = global.TableBigVarcharMeta{
			ColumnName: columnName,
			ColumnType: columnType,
		}
	}
	return
}

// indexNameColumnMerge 每个表的索引列数据信息合并及抽取
func (or TableInfoMeta) indexNameColumnMerge(s global.TablesMetaInfoInput) global.IndexMerge {
	var indexName = make(map[string][]global.IndexColumn)
	var indexType = make(map[string]string)
	var indexColumnSeq = make(map[string][]string)
	var indexNameOrderBy []string
	for _, v := range s.IndexColumn {
		if _, ok := indexType[v.IndexName]; !ok {
			var tmpIndexType string
			switch v.ColumnKey {
			case "P":
				tmpIndexType = "pri"
				v.IndexName = "PRIMARY"
			case "U":
				tmpIndexType = "uni"
			case "M":
				tmpIndexType = "mul"
			}
			indexType[v.IndexName] = tmpIndexType
			indexNameOrderBy = append(indexNameOrderBy, v.IndexName)
		}
		if v1, ok := indexName[v.IndexName]; ok {
			indexName[v.IndexName] = append(v1, v)
		} else {
			indexName[v.IndexName] = []global.IndexColumn{v}
		}
		if v1, ok := indexColumnSeq[v.IndexName]; ok {
			indexColumnSeq[v.IndexName] = append(v1, v.ColumnSeq)
		} else {
			indexColumnSeq[v.IndexName] = []string{v.ColumnSeq}
		}
	}
	return global.IndexMerge{
		Schema:           s.TableInfo.Schema,
		Table:            s.TableInfo.Table,
		IndexColumn:      indexName,
		IndexType:        indexType,
		OrderByIndexName: indexNameOrderBy,
		IndexColumnSeq:   indexColumnSeq,
	}
}

func (or TableInfoMeta) IndexMerge(s global.TablesMetaInfoInput) global.IndexMerge {
	if len(s.IndexColumn) == 0 {
		return global.IndexMerge{}
	}
	//索引列处理，联合索引进行列合并
	//去除主键索引列、唯一索引列、普通索引列的所有列明
	p := or.indexNameColumnMerge(s)
	return p
}

type columnSumGroupS struct {
	singleColumn   []string //索引名
	multipleColumn []string //索引名
}

// 根据索引列数分为单列索引和多列索引
func (or TableInfoMeta) columnSumGroup(i global.IndexMerge) (q columnSumGroupS) {
	for _, indexName := range i.OrderByIndexName {
		if v, ok := i.IndexColumn[indexName]; ok {
			if len(v) == 1 {
				q.singleColumn = append(q.singleColumn, indexName)
			} else if len(v) > 1 {
				q.multipleColumn = append(q.multipleColumn, indexName)
			}
		}
	}
	return
}

type indexTypeGroupS struct {
	Primary   []string `json:"primary"`   //主键索引名
	UniqueKey []string `json:"uniqueKey"` //唯一索引名
	Key       []string `json:"key"`       //普通索引名
}

// 针对指定的索引名集合，做索引类型做聚合
func (or TableInfoMeta) indexTypeGroup(indexGarth []string, i global.IndexMerge) (q indexTypeGroupS) {
	for _, v := range indexGarth {
		if key, ok := i.IndexType[v]; ok {
			switch key {
			case "pri":
				q.Primary = append(q.Primary, v)
			case "uni":
				q.UniqueKey = append(q.UniqueKey, v)
			case "mul":
				q.Key = append(q.Key, v)
			}
		}
	}
	return
}

// 针对指定的索引名集合，做索引类型做聚合
func (or TableInfoMeta) columnTypeGroup(indexName string, i global.IndexMerge) (q []columnTypeGroupS) {
	if columnGarth, ok := i.IndexColumn[indexName]; ok {
		for _, v := range columnGarth {
			q = append(q, columnTypeGroupS{
				name:       v.ColumnName,
				class:      v.ColumnType,
				null:       v.Null,
				dispersion: v.Cardinality,
			})
		}
	}
	return
}
func (or TableInfoMeta) columnTypePriority(q []columnTypeGroupS) (r []int) {
	if len(q) == 0 {
		return
	}
	for _, v := range q {
		switch strings.Split(strings.ToUpper(v.class), "(")[0] {
		case "NUMBER": //整数数值类型
			r = append(r, 1)
		case "FLOAT", "BINARY_FLOAT", "BINARY_DOUBLE": //单精度和双精度的浮点数类型
			r = append(r, 1)
		case "DATE", "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH LOCAL TIME ZONE", "INTERVAL DAY TO SECOND", "TO_CHAR(SYSDATE,'YYYYMMDD')": //时间类型
			r = append(r, 2)
		case "CHAR", "NCHAR": //字符串定长
			r = append(r, 3)
		case "VARCHAR2", "NVARCHAR2": //字符串变长
			r = append(r, 3)
		default:
			r = append(r, 1000)
		}
	}
	return
}
func (or TableInfoMeta) columnNullGroup(q []columnTypeGroupS) (r []int) {
	for _, v := range q {
		if strings.EqualFold(v.null, "no") {
			r = append(r, 0)
		} else {
			r = append(r, 2)
		}
	}
	return
}
func (or TableInfoMeta) columnDispersion(q []columnTypeGroupS) (r []int) {
	for _, v := range q {
		if len(v.dispersion) == 0 {
			r = append(r, 0)
			continue
		}
		if t, err := strconv.Atoi(v.dispersion); err != nil {
			r = append(r, 0)
		} else {
			r = append(r, t)
		}
	}
	return
}

func (or TableInfoMeta) costChoice(i global.IndexMerge) (result global.IndexColumnSumGroup, err error) {
	var (
		indexTypeGarth map[string]any
	)
	type kv struct {
		key   string
		value int
	}

	a := or.columnSumGroup(i)
	for k1, indexGarth := range [][]string{
		a.singleColumn, a.multipleColumn,
	} {
		q := global.IndexPrioritySort{}
		//比较单、多列索引
		if indexTypeGarth, err = global.StructToMapAny(or.indexTypeGroup(indexGarth, i)); err != nil {
			return
		}
		var pp = make(map[string]any)
		for k, v := range indexTypeGarth {
			var (
				indexNameGarth   []string
				sortedDispersion []kv
				dispersionPairs  []map[string]any
			)
			if v == nil {
				continue
			}
			switch fmt.Sprintf("%v", reflect.TypeOf(v)) {
			case "[]string":
				indexNameGarth = v.([]string)
			}
			if len(indexNameGarth) == 0 {
				continue
			}
			//按照列的离散度进行排序
			for _, indexName := range indexNameGarth {
				o := or.columnDispersion(or.columnTypeGroup(indexName, i))
				total := func() (sum int) {
					for _, dispersion := range o {
						sum += dispersion
					}
					return
				}()
				sortedDispersion = append(sortedDispersion, kv{
					key:   indexName,
					value: total,
				})
			}
			sort.Slice(sortedDispersion, func(m, j int) bool {
				if sortedDispersion[m].value == sortedDispersion[j].value { //离散度相同
					//先比较数据类型优先级
					srcClassPriority := or.columnTypePriority(or.columnTypeGroup(sortedDispersion[m].key, i))
					srcClassTotal := func() (sum int) {
						for _, priorityValue := range srcClassPriority {
							sum += priorityValue
						}
						return
					}()
					dstClassPriority := or.columnTypePriority(or.columnTypeGroup(sortedDispersion[j].key, i))
					dstClassTotal := func() (sum int) {
						for _, priorityValue := range dstClassPriority {
							sum += priorityValue
						}
						return
					}()
					switch {
					case srcClassTotal > dstClassTotal:
						return sortedDispersion[m].value < sortedDispersion[j].value
					case srcClassTotal < dstClassTotal:
						return sortedDispersion[m].value > sortedDispersion[j].value
					}
					//再比较null值优先级
					srcNullPriority := or.columnNullGroup(or.columnTypeGroup(sortedDispersion[m].key, i))
					srcNullTotal := func() (sum int) {
						for _, priorityValue := range srcNullPriority {
							sum += priorityValue
						}
						return
					}()
					dstNullPriority := or.columnNullGroup(or.columnTypeGroup(sortedDispersion[j].key, i))
					dstNullTotal := func() (sum int) {
						for _, priorityValue := range dstNullPriority {
							sum += priorityValue
						}
						return
					}()
					switch {
					case srcNullTotal > dstNullTotal:
						return sortedDispersion[m].value < sortedDispersion[j].value
					case srcNullTotal < dstNullTotal:
						return sortedDispersion[m].value > sortedDispersion[j].value
					}
				}
				return sortedDispersion[m].value > sortedDispersion[j].value // 按 value 倒序
			})
			for _, subDispersion := range sortedDispersion {
				dispersionPairs = append(dispersionPairs, map[string]any{"name": subDispersion.key, "column": func() (m []string) {
					for _, col := range or.columnTypeGroup(subDispersion.key, i) {
						m = append(m, col.name)
					}
					return
				}()})
			}
			pp[k] = dispersionPairs
		}
		var jsonData []byte
		if jsonData, err = json.Marshal(pp); err != nil {
			return
		}
		if err = json.Unmarshal(jsonData, &q); err != nil {
			return
		}
		switch k1 {
		case 0:
			result.Single = q
		case 1:
			result.Multiple = q
		}
	}
	return
}
func (or TableInfoMeta) IndexOptimizerTrace(s global.TablesMetaInfoInput) (any, error) {
	var (
		result global.IndexColumnSumGroup
		err    error
	)
	if len(s.IndexColumn) == 0 {
		return nil, nil
	}
	//索引列处理，联合索引进行列合并
	//去除主键索引列、唯一索引列、普通索引列的所有列明
	f1 := or.IndexMerge(s)
	if global.IsEmptyStruct(f1) {
		//return global.IndexSelection{}
	}
	if result, err = or.costChoice(f1); err != nil {
		return nil, err
	}
	return result, nil
}

// 一个索引的包含的列类型对比
func (or TableInfoMeta) typeSelection(tsl []global.IndexColumn) []int {
	var typeDefinition = map[string]int{"int": 1, "time": 2, "string": 3, "text": 4}
	var intType = []string{"NUMBER", "INTEGER", "FLOAT", "REAL", "DOUBLE", "DECIMAL", "BINARY_FLOAT", "BINARY_DOUBLE"}
	var timeType = []string{"DATE", "TIMESTAMP", "INTERVAL YEAR", "INTERVAL DAY"}
	var stringType = []string{"CHAR", "VARCHAR2", "NCHAR", "NVARCHAR2"}
	var typeSeq []int
	for k, v := range tsl {
		//前导列限制，后期可以放开
		if k > 0 {
			continue
		}
		var exist = false
		for _, v1 := range intType {
			if strings.HasPrefix(strings.ToUpper(v.ColumnType), v1) {
				typeSeq = append(typeSeq, typeDefinition["int"])
				exist = true
			}
		}
		for _, v1 := range timeType {
			if strings.HasPrefix(strings.ToUpper(v.ColumnType), v1) {
				typeSeq = append(typeSeq, typeDefinition["time"])
				exist = true
			}
		}
		for _, v1 := range stringType {
			if strings.HasPrefix(strings.ToUpper(v.ColumnType), v1) {
				typeSeq = append(typeSeq, typeDefinition["string"])
				exist = true
			}
		}
		if !exist {
			typeSeq = append(typeSeq, 100)
		}
	}
	return typeSeq
}

// 多个索引的数据类型比较，按照规则选择一个或多个相同的
func (or TableInfoMeta) typeSum(ll map[string][]int) (iName string) {
	iValue := 10000
	var iSlice []int
	for k, v := range ll {
		var iSum int
		for _, v1 := range v {
			iSum += v1
		}
		if iSum < iValue {
			iName = k
			iSlice = v
			iValue = iSum
		} else if iSum == iValue {
			for k2, v2 := range iSlice {
				if v2 < v[k2] {
					iName = k
					iSlice = v
					iValue = iSum
				}
			}
		}
	}
	return
}

// mulCardinality 普通索引通过离散度来进行筛选
func (or TableInfoMeta) mulCardinality(mulSelection map[string] /*indexType*/ []string /*indexName*/, i global.IndexMerge) any {
	//先检查索引前导列的离散度（第一列离散度值，默认选择一个离散度最高的，如果离散度比较低，建议先analyze table一下）
	var cardinality uint64 = 0
	event := "[mulCardinality]"
	var indexName string
	if len(mulSelection) == 0 {
		return nil
	}
	for _, v := range mulSelection {
		for _, v1 := range v {
			var curryIndexCardinality uint64 = 0
			for _, v2 := range i.IndexColumn[v1] {
				if len(v2.Cardinality) > 0 {
					if parseUint, err2 := strconv.ParseUint(v2.Cardinality, 10, 64); err2 != nil {
						WLog.Warn(fmt.Sprintf("(%d) %s string strconv uint64 fail. string is {%v}, error info is %v", 1, event, v2.Cardinality, err2))
					} else {
						if parseUint > curryIndexCardinality {
							curryIndexCardinality = parseUint
						}
					}
				}
			}
			if curryIndexCardinality >= cardinality {
				cardinality = curryIndexCardinality
				indexName = v1
			}
		}
	}
	return map[string]any{"indexName": indexName, "cardinality": cardinality}
}
func (or TableInfoMeta) keyChoice(i global.IndexMerge) global.IndexSelection {
	//候选索引,先选择主键索引且索引列小于3，然后是唯一索引、普通索引
	var (
		candidateIndex                                             = map[string][]string{"pri": {}, "uni": {}, "mui": {}}
		cardinalityGather                                          []any
		maxCardinalityValue                                        uint64
		cardinalityMaxGather                                       = make(map[string]uint64)
		optimalIndexColumnType                                     []string
		multipleMaxCardinalityGather                               map[string] /*索引名*/ []int //类型定义值
		priSingleSelection, uniSingleSelection, muiSingleSelection = make(map[string][]string), make(map[string][]string), make(map[string][]string)
		priMullSelection, uniMullSelection, muiMullSelection       = make(map[string][]string), make(map[string][]string), make(map[string][]string)
	)
	for k, v := range i.IndexType {
		for _, v1 := range []string{"pri", "uni", "mul"} {
			if strings.EqualFold(v1, v) {
				if len(i.IndexColumn[k]) > 0 {
					candidateIndex[v1] = append(candidateIndex[v1], k)
				}
			}
		}
	}
	//没有索引则退出
	exit := true
	for _, v := range candidateIndex {
		if len(v) > 0 {
			exit = false
		}
	}
	if exit {
		return global.IndexSelection{}
	}
	//筛选主键或者唯一索引为单列的
	for k, v := range candidateIndex {
		var fp, fu, fm []string
		var mp, mu, mm []string
		for _, v1 := range v {
			if v2, ok := i.IndexColumn[v1]; ok {
				if len(v2) == 1 {
					if strings.EqualFold(k, "pri") {
						fp = append(fp, v1)
					} else if strings.EqualFold(k, "uni") {
						fu = append(fu, v1)
					} else if strings.EqualFold(k, "mul") {
						fm = append(fm, v1)
					}
				} else {
					if strings.EqualFold(k, "pri") {
						mp = append(mp, v1)
					} else if strings.EqualFold(k, "uni") {
						mu = append(mu, v1)
					} else if strings.EqualFold(k, "mul") {
						mm = append(mm, v1)
					}
				}
			}
		}
		if len(fp) > 0 {
			priSingleSelection[k] = fp
		}
		if len(fu) > 0 {
			uniSingleSelection[k] = fu
		}
		if len(fm) > 0 {
			muiSingleSelection[k] = fm
		}
		if len(mp) > 0 {
			priMullSelection[k] = mp
		}
		if len(mu) > 0 {
			uniMullSelection[k] = mu
		}
		if len(mm) > 0 {
			muiMullSelection[k] = mm
		}
	}
	cardinalityGather = append(cardinalityGather, or.mulCardinality(priSingleSelection, i))
	cardinalityGather = append(cardinalityGather, or.mulCardinality(priMullSelection, i))
	cardinalityGather = append(cardinalityGather, or.mulCardinality(uniSingleSelection, i))
	cardinalityGather = append(cardinalityGather, or.mulCardinality(uniMullSelection, i))
	cardinalityGather = append(cardinalityGather, or.mulCardinality(muiSingleSelection, i))
	cardinalityGather = append(cardinalityGather, or.mulCardinality(muiMullSelection, i))
	for _, v := range cardinalityGather {
		if v == nil {
			continue
		}
		curryCardinalityValue := v.(map[string]any)["cardinality"].(uint64)
		curryIndexName := fmt.Sprintf("%v", v.(map[string]any)["indexName"])
		if curryCardinalityValue > maxCardinalityValue {
			cardinalityMaxGather = map[string]uint64{
				curryIndexName: curryCardinalityValue,
			}
			maxCardinalityValue = curryCardinalityValue
		} else if curryCardinalityValue == maxCardinalityValue {
			cardinalityMaxGather[curryIndexName] = curryCardinalityValue
		}
	}
	//处理多个离散度相同的索引名
	multipleMaxCardinalityGather = make(map[string][]int)
	for k := range cardinalityMaxGather {
		if v1, ok := i.IndexColumn[k]; ok {
			multipleMaxCardinalityGather[k] = or.typeSelection(v1)
		}
	}
	//按照数据类型规则找到最优的
	optimalIndexName := or.typeSum(multipleMaxCardinalityGather)
	for _, v := range i.IndexColumn[optimalIndexName] {
		optimalIndexColumnType = append(optimalIndexColumnType, v.ColumnType)
	}
	return global.IndexSelection{
		IndexType:        i.IndexType[optimalIndexName],
		IndexColumn:      i.IndexColumn[optimalIndexName],
		IndexName:        optimalIndexName,
		IndexCardinality: cardinalityMaxGather[optimalIndexName],
		IndexColumnType:  optimalIndexColumnType,
	}
}

func (or TableInfoMeta) IndexChoice(s global.TablesMetaInfoInput) (result any, err error) {
	//var (
	//	Event = "[IndexChoice]"
	//)
	if len(s.IndexColumn) == 0 {
		return
	}
	//索引列处理，联合索引进行列合并
	//去除主键索引列、唯一索引列、普通索引列的所有列明
	f1 := or.IndexMerge(s)
	if global.IsEmptyStruct(f1) {
		return
	}
	p := or.keyChoice(f1)
	return p, nil
}

type indexConstraintTypeS struct {
	SchemaName     string `json:"databaseName"`
	TableName      string `json:"tableName"`
	IndexName      string `json:"indexName"`
	ConstraintType string `json:"constraintType"`
}

func (or TableInfoMeta) indexConstraintType(s global.TablesMetaInfoInput) (any, error) {
	var (
		event         = "[OracleIndexConstraintType]"
		indexNameData []indexConstraintTypeS
		err           error
	)
	strSql := fmt.Sprintf("select "+
		"OWNER as \"databaseName\", \n"+
		"TABLE_NAME as \"tableName\", \n"+
		"CO.INDEX_NAME as \"indexName\", \n"+
		"CO.CONSTRAINT_TYPE as \"constraintType\" \n"+
		"from "+
		"ALL_CONSTRAINTS \"CO\" "+
		"where OWNER IN ('%v') AND TABLE_NAME IN ('%v')", s.TableInfo.Schema, s.TableInfo.Table)

	active := ETL.InstanceActiveS{DBType: DBType, DB: s.DB["single"], SqlMode: true}
	var f1 any
	if _, f1, err = active.SqlQuery(strSql, ETL.SMap); err != nil {
		err = errors.New(fmt.Sprintf("%v get Failed to obtain index information. error is:%v", event, err))
		return nil, err
	}
	if fmt.Sprintf("%v", reflect.TypeOf(f1)) == "[]" {
		return nil, err
	}
	for _, v := range f1.([]map[string]interface{}) {
		var dd = indexConstraintTypeS{}
		var jsonData []byte
		for g, j := range v {
			v[g] = fmt.Sprintf("%v", j)
		}
		jsonData, err = json.Marshal(v)
		if err != nil {
			err = errors.New(fmt.Sprintf("%v []map[string]any strconv json fail. Execution process{strconv data:%v error:%v}", event, v, err))
			return nil, err
		}
		// 反序列化JSON字符串到结构体
		err = json.Unmarshal(jsonData, &dd)
		if err != nil {
			err = errors.New(fmt.Sprintf("%v []byte json unmarshal struct fail. Execution process{strconv data:%v error:%v}", event, string(jsonData), err))
			return nil, err
		}
		indexNameData = append(indexNameData, dd)
	}
	return indexNameData, err
}

type indexColumnS struct {
	SchemaName string `json:"databaseName"`
	TableName  string `json:"tableName"`
	IndexName  string `json:"indexName"`
	ColumnName string `json:"columnName"`
	ColumnSeq  string `json:"columnSeq"`
}

func (or TableInfoMeta) indexColumn(s global.TablesMetaInfoInput) (any, error) {
	var (
		event         = "[OracleIndexName]"
		indexNameData []indexColumnS
		err           error
	)
	strSql := fmt.Sprintf("select "+
		"IC.TABLE_OWNER AS \"databaseName\","+
		"IC.TABLE_NAME as \"tableName\","+
		"IC.INDEX_NAME AS \"indexName\","+
		"IC.COLUMN_POSITION AS \"columnSeq\","+
		"IC.COLUMN_NAME as \"columnName\" "+
		"from "+
		"\"ALL_IND_COLUMNS\" \"IC\" "+
		"WHERE "+
		"IC.TABLE_OWNER IN ('%v') AND IC.TABLE_NAME IN ('%v') "+
		"order by IC.INDEX_NAME,IC.COLUMN_POSITION", s.TableInfo.Schema, s.TableInfo.Table)
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.DB["single"], SqlMode: true}
	var f1 any
	if _, f1, err = active.SqlQuery(strSql, ETL.SMap); err != nil {
		err = errors.New(fmt.Sprintf("%v sql exec fail. Execution process:{sql:%v,err:%v}", event, strSql, err))
		return nil, err
	}
	if fmt.Sprintf("%v", reflect.ValueOf(f1)) == "[]" {
		return nil, err
	}
	for _, v := range f1.([]map[string]interface{}) {
		var dd = indexColumnS{}
		var jsonData []byte
		jsonData, err = json.Marshal(v)
		if err != nil {
			err = errors.New(fmt.Sprintf("%v []map[string]any strconv json fail. Execution process{strconv data:%v error:%v}", event, v, err))
			return nil, err
		}
		// 反序列化JSON字符串到结构体
		err = json.Unmarshal(jsonData, &dd)
		if err != nil {
			err = errors.New(fmt.Sprintf("%v []byte json unmarshal struct fail. Execution process{strconv data:%v error:%v}", event, string(jsonData), err))
			return nil, err
		}
		indexNameData = append(indexNameData, dd)
	}
	return indexNameData, err
}

type allIndexesS struct {
	SchemaName    string `json:"databaseName"`
	TableName     string `json:"tableName"`
	IndexName     string `json:"indexName"`
	NonUnique     string `json:"nonUnique"`
	Cardinality   string `json:"cardinality"`
	IndexCategory string `json:"indexCategory"`
}

func (or TableInfoMeta) allIndexes(s global.TablesMetaInfoInput) (any, error) {
	var (
		event        = "[OracleAllIndexes]"
		allIndexData []allIndexesS
		err          error
	)
	strSql := fmt.Sprintf("SELECT "+
		"I.TABLE_OWNER AS \"databaseName\","+
		"I.TABLE_NAME as \"tableName\","+
		"I.INDEX_NAME as \"indexName\","+
		"I.UNIQUENESS AS \"nonUnique\","+
		"I.DISTINCT_KEYS AS \"cardinality\","+
		"I.PARTITIONED AS \"indexCategory\" "+
		"FROM \"ALL_INDEXES\" \"I\" "+
		"WHERE I.TABLE_OWNER IN ('%v') AND I.TABLE_NAME IN ('%v')", s.TableInfo.Schema, s.TableInfo.Table)
	active := ETL.InstanceActiveS{DBType: DBType, DB: s.DB["single"], SqlMode: true}
	var f1 any
	if _, f1, err = active.SqlQuery(strSql, ETL.SMap); err != nil {
		err = errors.New(fmt.Sprintf("%v sql exec fail. Execution process:{sql:%v,err:%v}", event, strSql, err))
		return nil, err
	}
	for _, v := range f1.([]map[string]interface{}) {
		var dd = allIndexesS{}
		var jsonData []byte
		jsonData, err = json.Marshal(v)
		if err != nil {
			err = errors.New(fmt.Sprintf("%v []map[string]any strconv json fail. Execution process{strconv data:%v error:%v}", event, v, err))
			return nil, err
		}
		// 反序列化JSON字符串到结构体
		err = json.Unmarshal(jsonData, &dd)
		if err != nil {
			err = errors.New(fmt.Sprintf("%v []byte json unmarshal struct fail. Execution process{strconv data:%v error:%v}", event, string(jsonData), err))
			return nil, err
		}
		switch {
		case strings.EqualFold(dd.IndexCategory, "NO"):
			dd.IndexCategory = "global"
		case strings.EqualFold(dd.IndexCategory, "YES"):
			dd.IndexCategory = "local"
		}
		allIndexData = append(allIndexData, dd)
	}
	return allIndexData, err
}

/*
IndexColumn 查询Oracle库下指定表的索引统计信息
*/
func (or TableInfoMeta) IndexColumn(s global.TablesMetaInfoInput) (result any, err error) {
	var (
		indexData               []global.IndexColumn
		indexConstraintTypeData []indexConstraintTypeS
		indexColumnData         []indexColumnS
		allIndexData            []allIndexesS
		allColumnData           []global.TableMeta
		p                       TableColumn
		event                   = "[oracleIndexColumn]"
	)
	if result, err = p.All(global.GetColumnTypeInput{DB: s.DB, TableInfo: s.TableInfo}); err != nil || result == nil {
		if err != nil {
			err = errors.New(fmt.Sprintf("%v get Failed to obtain index information. error is:%v", event, err))
		}
		return
	} else {
		allColumnData = result.(global.AllReturnResult).Result
	}
	if result, err = or.indexConstraintType(s); err != nil { //可以为空
		err = errors.New(fmt.Sprintf("%v get Failed to obtain index information. error is:%v", event, err))
		return nil, err
	}
	if result != nil {
		indexConstraintTypeData = result.([]indexConstraintTypeS)
	}
	if result, err = or.indexColumn(s); err != nil {
		err = errors.New(fmt.Sprintf("%v get Failed to obtain index information. error is:%v", event, err))
		return nil, err
	}
	if result == nil {
		return nil, err
	}
	indexColumnData = result.([]indexColumnS)
	if result, err = or.allIndexes(s); err != nil || result == nil {
		return nil, err
	} else {
		allIndexData = result.([]allIndexesS)
	}
	for _, v := range allIndexData {
		var dd = global.IndexColumn{
			Schema:        v.SchemaName,
			Table:         v.TableName,
			IndexName:     v.IndexName,
			IndexCategory: v.IndexCategory,
			Cardinality:   v.Cardinality,
		}
		//索引类型
		var constraintType string
		for _, constraint := range indexConstraintTypeData {
			if v.SchemaName == constraint.SchemaName && v.TableName == constraint.TableName {
				if v.IndexName == constraint.IndexName {
					constraintType = constraint.ConstraintType
					break
				}
			}
		}
		dd.ColumnKey = func() string {
			switch {
			case strings.TrimSpace(constraintType) == "P":
				return "P"
			case strings.TrimSpace(constraintType) == "U":
				return "U"
			default:
				if strings.EqualFold(v.NonUnique, "UNIQUE") {
					return "U"
				} else {
					return "M"
				}
			}
		}()
		if dd.ColumnKey == "P" {
			dd.IndexName = "PRIMARY"
		}
		//索引列
		for _, column := range indexColumnData {
			if v.SchemaName == column.SchemaName && v.TableName == column.TableName && v.IndexName == column.IndexName {
				dd.ColumnName = column.ColumnName
				for _, columnInfo := range allColumnData {
					if columnInfo.ColumnName == column.ColumnName {
						dd.ColumnSeq = fmt.Sprintf("%v", columnInfo.ColumnSeq)
						dd.ColumnType = columnInfo.ColumnType
						indexData = append(indexData, dd)
						break
					}
				}
			}
		}
	}
	return indexData, err
}

func (or TableInfoMeta) DropIndexSql(_ global.TablesMetaInfoInput) (any, error) {
	return global.AlterSqlResult{}, nil
}
func (or TableInfoMeta) AddIndexSql(_ global.TablesMetaInfoInput) (any, error) {
	return global.AlterSqlResult{}, nil
}
func (or TableInfoMeta) ModifyIndexSql(_ global.TablesMetaInfoInput) (any, error) {
	return global.AlterSqlResult{}, nil
}
